<template>
    <el-container>
        <el-main>
            <div class="fixed-box">
                <span class="fixed-box-label">显示/隐藏热力图</span>
                <el-switch v-model="heatmapDisplay" active-color="#13ce66" inactive-color="#ff4949" />
            </div>
            <div ref="mapDiv" >
                <el-amap class="amap-box" vid="amap" ref="map"
                         viewMode="3D"
                         :pitch="pitch"
                         rotateEnable="true"
                         mapStyle="light"
                         :amap-manager="amapManager"
                         :center="center"
                         :plugin="plugin"
                         :defaultCursor="'default'"
                         :events="events"
                         :zoom="zoom">
                    <div v-if="!heatmapDisplay">
                        <div v-if="!eMarkersExist">
                            <el-amap-circle-marker v-for="(item,index) in areaMarkers" :key="'key_' + item.area.areaId" v-if="item.area.enable"
                                                   :index="item.area.areaId"
                                                   :extData="item"
                                                   :vid="'amap-marker_' + item.area.areaId"
                                                   :center="[item.area.areaLng,item.area.areaLat]"
                                                   :radius="item.area.count*3 + 1"
                                                   :strokeColor="'white'"
                                                   :strokeWeight=2
                                                   :strokeOpacity=0.5
                                                   :fillColor="'rgba(0,0,255,1)'"
                                                   :fillOpacity=0.5
                                                   :zIndex=10
                                                   :bubble=true
                                                   :events="item.events"/>
                        </div>
                        <el-amap-info-window autoMove="true"
                                             :position="window.position"
                                             :visible="window.visible"
                                             :content="window.content"
                                             :events="window.events"
                                             :offset="window.offset"/>
                    </div>
                </el-amap>
            </div>
        </el-main>
    </el-container>
</template>

<script>
import {mapState,mapActions} from "vuex";
import {AMapManager} from "vue-amap";

let amapManager = new AMapManager();

export default {
    name: "DataDistribution",
    data() {
        let _this = this;
        return {
            map: null,
            heatmap: null,
            heatmapDisplay: false,
            center: [104.06584, 30.65787],
            zoom: 4,
            pitch: 0,                   //地图俯仰角度，有效范围为0-83度
            amapManager,
            plugin: [
                'AMap.Autocomplete',
                'AMap.PlaceSearch',
                'AMap.Scale',
                'AMap.ToolBar'
            ],
            events: {
                zoomend() {
                    // console.log(amapManager.getMap().getZoom())
                    if (amapManager.getMap().getZoom() >= 13) {
                        if (!_this.eMarkersExist) {
                            _this.eMarkersExist = true;
                        }
                    } else {
                        if (_this.eMarkersExist) {
                            _this.eMarkersExist = false;
                            _this.initAreaMarkers();
                        }
                        //移除3D图层上的所有对象
                        if(_this.object3DLayer){
                            _this.object3DLayer.clear();
                        }
                        //移除单一对象
                        // _this.object3DLayer.remove(cylinder);
                    }
                }
            },
            areaMarkers: [],                    //记录地图上的区域标记点
            eMarkersExist: false,               //用于判断设备标记点是否生成,避免重复
            windows: [],                        //用于存储区域信息
            window: {                           //用于控制当前info-window的显示内容
                position: [104.06584, 30.65787],
                visible: false,
                content: "default",
                offset: [0, -30],
                events: {
                    close() {
                        _this.window.visible = false
                    }
                }
            },
            equipmentArray: [],                 //存放选择区域所部属的设备数据,用于3D柱体的生成和详细数据显示
            object3DLayer : null,               //3D地图层
            meshArray: [],                      //存放3D柱体实例的数组
            topColor: [123/255,217/255,122/255,0.9],            //柱体顶部颜色,Object3D.Mesh的color分别是[r,g,b,a],每个分量只支持[0-1]区间
            topFaceColor: [123/255,217/255,122/255,0.4],        //柱体表面颜色
            bottomColor: [66/255,147/255,64/255,0.9],           //柱体底部颜色
            sTopColor: [233/255, 185/255, 14/255, 0.9],         //选中时的柱体顶部颜色
            sTopFaceColor: [233/255, 185/255, 14/255, 0.4],     //选中时的柱体表面颜色
            sBottomColor: [192/255, 175/255, 118/255, 0.9],     //选中时的柱体底部颜色
            segment: 32                         //Object3D对象中边角面的个数
        }
    },
    computed: {
        ...mapState({
            areaData: state => state.active.areaData,
            equipmentData: state => state.active.equipmentData,
            baseStatisticsData: state => state.active.baseStatisticsData
        })
    },
    methods: {
        ...mapActions([
            'getEquipmentsByAreaID'
        ]),
        initAMap(){
            //获取map实例
            this.map = amapManager.getMap();
            let _this = this;
            //创建一个右键菜单
            let contextMenu = new AMap.ContextMenu();
            //添加菜单item
            contextMenu.addItem("放大一级",function (){
                _this.map.zoomIn();
            },0);
            contextMenu.addItem("缩小一级",function (){
                _this.map.zoomOut();
            },1);
            contextMenu.addItem("恢复默认缩放",function (){
                _this.map.setZoomAndCenter(4,_this.center);
                //vue-amap中的:zoom并不是双向绑定的,所以虽然通过setZoomAndCenter()改变了amap的zoom值,但页面data中的zoom值仍然需要手动更改(vue-amap是真的不行...)
                //pitch也一样...
                _this.zoom = 4;
                _this.pitch = 0;
                _this.map.setPitch(0);
                _this.map.setRotation(0);
            },2);
            //地图实例绑定右键事件打开菜单
            this.map.on('rightclick',function (e){
                contextMenu.open(_this.map,e.lnglat);
            })
            //热力图
            this.createHeatMap();
        },
        initAreaMarkers() {
            let _this = this;
            //先清空数组,避免每次改变都会生成一组新的marker却用的同样的index,导致报错
            this.areaMarkers = [];
            this.window.visible = false;
            this.window.content = "default";
            //将每个地区的地址分类存入areaMarkers
            for (let item of this.areaData) {
                this.areaMarkers.push({
                    area: item,
                    events: {
                        mouseover(e) {
                            //获取绑定在el-amap-marker上的数据
                            let target = e.target.getExtData().area;
                            //设置点击逻辑
                            _this.window.position = [target.areaLng, target.areaLat];
                            _this.window.visible = true;
                            _this.window.content = '<div class="area-marker-info-window">' +
                                '                       <div>区域名称：' + target.areaName + '</div>' +
                                '                       <div>所属城区：' + target.areaRemarks + '</div>' +
                                '                       <div>部署数量：' + target.count + '</div>' +
                                '                       <div>发送频率：' + target.frequency + ' min/次</div>' +
                                '                       <div>创建日期：' + target.createDate + '</div>' +
                                '                   </div>'
                        },
                        mouseout(e) {
                            _this.window.visible = false;
                        },
                        dblclick(e) {
                            //获取绑定在el-amap-marker上的数据
                            let target = e.target.getExtData().area;
                            //获取map实例
                            /*let map = amapManager.getMap(); 修改1 */
                            //添加 Object3DLayer 图层，用于装载 Object3D 对象
                            _this.object3DLayer = new AMap.Object3DLayer();
                            _this.map.add(_this.object3DLayer);
                            //按地图缩放比例判断
                            if (_this.zoom > 12) {
                                //复位
                                _this.zoom = 4;
                                _this.pitch = 0;
                                _this.map.setRotation(0);
                            } else {
                                _this.center = [target.areaLng, target.areaLat];
                                //放大
                                _this.zoom = 16;
                                _this.pitch = 0;
                                _this.map.setRotation(0);
                                //关闭info-window
                                _this.window.visible = false;
                                //获取选中区域的设备相关信息
                                _this.getEquipmentsByAreaID(target.areaId).then(resp=>{
                                    //深拷贝
                                    _this.equipmentArray = JSON.parse(JSON.stringify(resp))
                                    //按equipment信息数组依次载入对应的柱体对象
                                    _this.initEquipmentMesh(_this.map);
                                    //添加Object3D对象的拾取事件
                                    _this.addObject3DSelectAction(_this.map,_this.object3DLayer);
                                })
                            }
                        }
                    }
                })
            }
        },
        initEquipmentMesh(map){
            for(let item of this.equipmentArray){
                //获取需要放置Mesh对象的坐标点
                let lnglat = new AMap.LngLat(item.positionLng,item.positionLat);
                //设置Mesh对象的中心点
                let center_3D = map.lngLatToGeodeticCoord(lnglat);
                //添加Mesh对象到3D柱体数组中
                this.meshArray.push(this.getMeshCylinder(center_3D, this.segment, 50 + item.collected, 500, this.topColor, this.topFaceColor, this.bottomColor, item));
            }
            //将柱体组实例载入3D地图层
            for(let item of this.meshArray){
                this.object3DLayer.add(item);
            }
        },
        getMeshCylinder(center, segment, height, radius, topColor, topFaceColor, bottomColor, equipment) {
            let cylinder = new AMap.Object3D.Mesh();
            let geometry = cylinder.geometry;
            //虽说叫圆柱,但其实是由众多边拼成的,segment代表边的数量
            let verticesLength = segment * 2;
            let path = [];
            for (let i = 0; i < segment; i++) {
                let angle = 2 * Math.PI * i / segment;
                let x = center.x + Math.cos(angle) * radius;
                let y = center.y + Math.sin(angle) * radius;
                path.push([x, y]);
                geometry.vertices.push(x, y, 0); //底部顶点
                geometry.vertices.push(x, y, -height); //顶部顶点

                geometry.vertexColors.push.apply(geometry.vertexColors, bottomColor); //底部颜色
                geometry.vertexColors.push.apply(geometry.vertexColors, topColor); //顶部颜色

                let bottomIndex = i * 2;
                let topIndex = bottomIndex + 1;
                let nextBottomIndex = (bottomIndex + 2) % verticesLength;
                let nextTopIndex = (bottomIndex + 3) % verticesLength;

                geometry.faces.push(bottomIndex, topIndex, nextTopIndex); //侧面三角形1
                geometry.faces.push(bottomIndex, nextTopIndex, nextBottomIndex); //侧面三角形2
            }
            // 构建柱体的顶层盖子,为了区分顶面点和侧面点使用不一样的颜色,所以需要独立的顶点
            for (let i = 0; i < segment; i++) {
                geometry.vertices.push.apply(geometry.vertices, geometry.vertices.slice(i * 6 + 3, i * 6 + 6)); //底部顶点
                geometry.vertexColors.push.apply(geometry.vertexColors, topFaceColor);
            }

            let triangles = AMap.GeometryUtil.triangulateShape(path);
            let offset = segment * 2;

            for (let v = 0; v < triangles.length; v += 3) {
                geometry.faces.push(triangles[v] + offset, triangles[v + 2] + offset, triangles[v + 1] + offset);
            }
            //如果使用了透明颜色，请设置true
            cylinder.transparent = true;
            //附带的设备信息
            cylinder.equipment = equipment;
            return cylinder;
        },
        addObject3DSelectAction(map,object3Dlayer){
            let _this = this;
            map.on('mousemove',function (e){
                //只有在放大显示equipmentMarker时才会进行对象判断
                if(_this.eMarkersExist){
                    //获取当前鼠标所在的像素点
                    let pixel = e.pixel;
                    let px = new AMap.Pixel(pixel.x,pixel.y);
                    //获取当前指向的Object3D对象
                    let targetObject = map.getObject3DByContainerPos(px,[object3Dlayer],false)||null;
                    //信息框控制
                    if(targetObject){
                        //获取对应的Equipment数据
                        let targetInfo = targetObject.object.equipment;
                        let state = '';
                        switch (targetInfo.state){
                            case 1: state = '正常';break;
                            case 2: state = '延迟';break;
                            case 3: state = '中断';break;
                            default: state = '未知错误';break;
                        }
                        //设置info-window的显示
                        _this.window.position = [targetInfo.positionLng,targetInfo.positionLat];
                        _this.window.visible = true;
                        _this.window.content = '<div class="equipment-marker-info-window">' +
                            '                       <div>设备编号：' + targetInfo.equipmentId + '</div>' +
                            '                       <div>剩余电量：' + targetInfo.power + ' %</div>' +
                            '                       <div>运行时长：' + targetInfo.runtime + ' h</div>' +
                            '                       <div>监测半径：' + targetInfo.radius + ' 米</div>' +
                            '                       <div>采集数据：' + targetInfo.collected + ' 条</div>' +
                            '                       <div>网络状态：' + state + '</div>' +
                            '                   </div>'
                        //选中Object3D对象的样式变化,参数为选中的Object3D的Mesh对象
                        _this.updateMesh(targetObject.object);
                    }else{
                        //重置所有Mesh的颜色
                        _this.initMeshsColor();
                        //按情况关闭info-window
                        if(_this.window.visible){
                            _this.window.visible = false;
                        }
                    }
                }
            })
        },
        initMeshsColor(){
            let _this = this;
            //重置所有Mesh对象的颜色
            this.meshArray.forEach(function (mesh){
                _this.updateMeshColor(mesh,_this.topColor,_this.topFaceColor,_this.bottomColor);
            })
        },
        updateMesh(targetMesh){
            //重置所有Mesh对象的颜色
            this.initMeshsColor()
            //从Array中获取选中的Mesh对象
            let mesh = this.meshArray.find(item=>item===targetMesh);
            //更新选中Mesh对象的颜色
            this.updateMeshColor(mesh,this.sTopColor,this.sTopFaceColor,this.sBottomColor);
        },
        updateMeshColor(mesh,topColor,topFaceColor,bottomColor) {
            let vertexColors = mesh.geometry.vertexColors;
            //替换颜色设置
            for (let i = 0; i < this.segment; i++) {
                //底部
                vertexColors.splice(i*8,4,bottomColor[0],bottomColor[1],bottomColor[2],bottomColor[3]);
                //顶部
                vertexColors.splice(i*8+4,4,topColor[0],topColor[1],topColor[2],topColor[3]);
                //侧边
                vertexColors.splice((this.segment*8)+i*4,4,topFaceColor[0],topFaceColor[1],topFaceColor[2],topFaceColor[3]);
            }
            //重新绘制图形对象
            mesh.needUpdate = true;
            mesh.reDraw();
        },
        createHeatMap(){
            let _this = this;
            this.map.plugin(["AMap.Heatmap"], function() {
                //因为地图初始化时heatmap数据可能还没获取到,因此设置一个1s的延迟
                setTimeout( _ => {
                    //初始化heatmap对象
                    _this.heatmap = new AMap.Heatmap(_this.map, {
                        //给定的热力点半径
                        radius: 30,
                        opacity: [0, 0.5]
                    });
                    //设置数据
                    _this.heatmap.setDataSet({
                        data: _this.baseStatisticsData.heatmap,
                        max: 1.8
                    });
                    //默认隐藏热力图
                    _this.heatmapHide();
                },1000);
            });
        },
        heatmapShow(){
            this.heatmap.show();
            this.heatmapDisplay = true;
        },
        heatmapHide(){
            this.heatmap.hide();
            this.heatmapDisplay = false;
        }
    },
    mounted() {
        this.initAreaMarkers();
    },
    beforeDestroy() {
        if(!this.map){
            return
        }
        this.map.destroy();
        this.map = null;
    },
    watch: {
        areaData(newValue, oldValue) {
            //初始化areaMarker数据
            this.initAreaMarkers();
            //初始化AMap
            this.initAMap();
        },
        heatmapDisplay(newValue,oldValue){
            if(newValue){
                this.heatmapShow();
            }else{
                this.heatmapHide();
            }
        }
    }
}
</script>

<style scoped>
/deep/ .el-main{
    padding: 0;
}

.amap-box{
    height: 750px;
}

/deep/ .amap-info-content{
    padding: 10px 18px 10px 18px;
}

/deep/ .area-marker-info-window{
    height: 100px;
    width: 360px;
    margin: 0;
    border-radius: 10px;
}

/deep/ .equipment-marker-info-window{
    height: 120px;
    width: 140px;
    margin: 0;
}

/deep/ .area-marker-info-window div{
    font-size: 14px;
    color: #99a9bf;
}

/deep/ .equipment-marker-info-window div{
    font-size: 14px;
    color: #99a9bf;
}

/deep/ .amap-info-close{
    top: 10px;
    right: 10px !important;
}

.fixed-box{
    display: flex;
    align-items: center;
    justify-content: space-around;
    position: fixed;
    width: 160px;
    height: 40px;
    right: 30px;
    bottom: 38px;
    z-index: 5;
    background-color: rgba(255, 255, 255, 0.9);
    border-radius: 0.4rem;
    box-shadow: 0 2px 6px 0 rgba(114,124,245,0.5);
    padding: 10px;
    opacity: 0.3;
    transition: opacity .6s;
}

.fixed-box:hover{
    opacity: 1;
}

.fixed-box-label{
    font-size: 14px;
    font-weight: bold;
    cursor: default;
}

</style>